package cn.dduan.fool.generic;

import java.util.ArrayList;
import java.util.List;

/**
 * 泛型
 * <p>
 * Java 泛型（generics）是 JDK 5 中引入的一个新特性
 * <p>
 * 方式：
 * 1、泛型类：class 类名<泛型类型1,...> {}
 * 2、泛型接口：interface 接口名<泛型类型> {}
 * 3、泛型方法：public <泛型类型> 返回类型 方法名（泛型类型 变量名） {}
 * <p>
 * 泛型通配符：
 * 1、无边界的通配符：<?>
 * 2、固定上边界的通配符：<? extends E> 表示的类型是E或者其子类型
 * 3、固定下边界的通配符：<? super E> 表示的类型是E或者其父类型
 * <p>
 * 优点：
 * 1、把运行时期的问题提前到了编译期间
 * 2、避免了强制类型转换
 *
 * 问题：
 * 1. 什么是泛型擦除？
 * 所谓的泛型擦除，官方名叫“类型擦除”
 * Java 的泛型是伪泛型，这是因为 Java 在编译期间，所有的类型信息都会被擦掉。
 * 也就是说，在运行的时候是没有泛型的
 * 2. 为什么要类型擦除呢？
 * 主要是为了向下兼容，因为JDK5之前是没有泛型的，为了让JVM保持向下兼容，就
 * 出了类型擦除这个策略
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/14
 */
public class MyGeneric {
    public static void main(String[] args) {
        // 泛型类
        GenericClass<String, Integer, Boolean> genericClass = new GenericClass<>("123", 321, false);
        System.out.println(genericClass.getValue());
        System.out.println(genericClass.getName());
        System.out.println(genericClass.getAge());

        // 泛型接口
        StringShowImpl stringShow = new StringShowImpl();
        stringShow.show("字符串");

        NumberShowImpl numberShow = new NumberShowImpl();
        numberShow.show(123);

        // 泛型方法
        MyGeneric myGeneric = new MyGeneric();
        myGeneric.genercMethod(false);
        myGeneric.genercMethod(111);

        // 无边界的通配符：<?>
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<String>();

        // 固定上边界通配符：<? extends E>
        // myGeneric.upperBoundary(new NumberShowImpl());
        List<? extends Number> list3 = new ArrayList<Number>();
        List<? extends Number> list4 = new ArrayList<Integer>();

        // 固定下边界的通配符：<? super E>
        List<? super String> list5 = new ArrayList<String>();
        List<? super String> list6 = new ArrayList<Object>();
    }

    /**
     * 泛型方法
     *
     * @param t
     * @return T
     * @author dduan
     * @version 1.0
     * @date 2023/4/14
     */
    private <T> T genercMethod(T t) {
        System.out.println(t.getClass());
        System.out.println(t);
        return t;
    }

    /**
     * 固定上边界的通配符
     *
     * @param t
     * @return T
     * @author dduan
     * @version 1.0
     * @date 2023/4/14
     */
    private <T extends NumberShowImpl> T upperBoundary(T t) {
        return t;
    }
}

/**
 * 泛型类
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/14
 */
class GenericClass<T, V, E> {
    private T value;
    private V name;
    private E age;

    public GenericClass(T value, V name, E age) {
        this.value = value;
        this.name = name;
        this.age = age;
    }

    public E getAge() {
        return age;
    }

    public V getName() {
        return name;
    }

    public T getValue() {
        return value;
    }
}

/**
 * 泛型接口
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/14
 */
interface GenericInterface<T> {
    void show(T value);
}

/**
 * 泛型接口实现
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/14
 */
class StringShowImpl implements GenericInterface<String> {
    @Override
    public void show(String value) {
        System.out.println(value);
    }
}

/**
 * 泛型接口实现
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/14
 */
class NumberShowImpl implements GenericInterface<Integer> {
    public NumberShowImpl() {
        System.out.println(this.getClass().getSimpleName());
    }

    @Override
    public void show(Integer value) {
        System.out.println(value);
    }
}

class TestNum extends NumberShowImpl {
    public TestNum() {
        System.out.println(super.getClass().getSimpleName() + "的子类");
    }
}